/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.designschema.synchronization;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import com.fasterxml.jackson.databind.JsonNode;
import com.inspur.edp.formserver.viewmodel.GspViewModel;
import com.inspur.edp.formserver.viewmodel.collection.ValueHelpConfigCollection;
import com.inspur.edp.formserver.viewmodel.common.ValueHelpConfig;
import com.inspur.edp.web.common.serialize.SerializeUtility;
import com.inspur.edp.web.common.utility.StringUtility;
import com.inspur.edp.web.designschema.elements.ComplexField;
import com.inspur.edp.web.designschema.elements.Field;
import com.inspur.edp.web.designschema.elements.Schema;
import com.inspur.edp.web.designschema.elements.SimpleField;
import com.inspur.edp.web.designschema.elements.editor.LookupEdit;
import com.inspur.edp.web.designschema.elements.type.EntityType;
import com.inspur.edp.web.designschema.elements.type.ObjectType;
import com.inspur.edp.web.designschema.synchronization.helplinkconfig.ExtractHelpLinkConfigFromJsonObject;
import com.inspur.edp.web.designschema.synchronization.helplinkconfig.HelpLinkConfigConvertor;
import com.inspur.edp.web.designschema.utils.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class Synchronizer {
    public final GspViewModel synchronize(JsonNode formObject, Schema schema, GspViewModel viewObject) {
        // 归集绑定表单变量的帮助。
        HashMap<String, LookupConfig> lookupConfigsFromVariables = this.gallaryLookupConfigs(formObject);
        // 如果ViewObject上无帮助定义，初始化帮助定义集合。
        if (viewObject.getValueHelpConfigs() == null) {
            viewObject.setValueHelpConfigs(new ValueHelpConfigCollection());
        }
        // 创建表单帮助定义字典。
        HashMap<String, ValueHelpConfig> latestValueHelpConfigs = new HashMap<>();

        // 由绑定表单变量的帮助创建帮助配置对象。
        for (LookupConfig lookupConfig : lookupConfigsFromVariables.values()) {
            if (!latestValueHelpConfigs.containsKey(lookupConfig.getUri())) {
                ValueHelpConfig tempVar = new ValueHelpConfig();
                tempVar.setElementId(lookupConfig.getUri());
                tempVar.setHelperId(lookupConfig.getLookupId());
                tempVar.setFilterExpression(lookupConfig.getCondition());
                // 设置映射后的参数配置项
                tempVar.setLinkedConfigs(HelpLinkConfigConvertor.convert(lookupConfig.getLinkConfig()));
                latestValueHelpConfigs.put(lookupConfig.getUri(), tempVar);
            }
        }
        // 带移除的已有帮助配置对象集合。
        ArrayList<ValueHelpConfig> valueHelpConfigsToRemove = new ArrayList<>();
        // 遍历ViewObject中已有的帮助配置对象，同步已添加数据，识别待删除数据。
        for (ValueHelpConfig valueHelpConfig : viewObject.getValueHelpConfigs()) {
            if (latestValueHelpConfigs.containsKey(valueHelpConfig.getElementId())) {
                ValueHelpConfig latestValueHelpConfig = latestValueHelpConfigs.get(valueHelpConfig.getElementId());
                valueHelpConfig.setHelperId(latestValueHelpConfig.getHelperId());
                valueHelpConfig.setFilterExpression(latestValueHelpConfig.getFilterExpression());
                // 设置映射后的参数配置项
                valueHelpConfig.setLinkedConfigs(latestValueHelpConfig.getLinkedConfigs());
                latestValueHelpConfigs.remove(valueHelpConfig.getElementId());
            } else {
                valueHelpConfigsToRemove.add(valueHelpConfig);
            }
        }
        // 在ViewObject中移除待删除的帮助配置对象。
        for (ValueHelpConfig valueHelpConfig : valueHelpConfigsToRemove) {
            viewObject.getValueHelpConfigs().remove(valueHelpConfig);
        }
        // 向ViewObject中添加新增的帮助配置对象。
        for (ValueHelpConfig lookupConfig : latestValueHelpConfigs.values()) {
            viewObject.getValueHelpConfigs().add(lookupConfig);
        }
        return viewObject;
    }

    private void gallaryLookupConfigsFromEntityList(List<Field> fields, HashMap<String, LookupConfig> lookupConfigs) {
        for (Field field : fields) {
            if (field instanceof SimpleField) {
                SimpleField simpleField = (SimpleField) field;
                if (simpleField.getEditor() instanceof LookupEdit) {
                    LookupEdit lookupEditor = (LookupEdit) simpleField.getEditor();
                    LookupConfig tempVar = new LookupConfig();
                    tempVar.setUri(String.join("/", lookupEditor.getDataSource().getUri().split("\\.")));
                    tempVar.setLookupId(lookupEditor.getHelpId());
                    lookupConfigs.put(lookupEditor.getDataSource().getUri(), tempVar);
                }
            } else if (field instanceof ComplexField) {
                ComplexField complexField = (ComplexField) field;
                if (complexField.getType() instanceof EntityType) {
                    EntityType entityType = (EntityType) complexField.getType();
                    this.gallaryLookupConfigsFromEntityList(entityType.getFields(), lookupConfigs);
                } else if (complexField.getType() instanceof ObjectType) {
                    ObjectType objectType = (ObjectType) complexField.getType();
                    this.gallaryLookupConfigsFromEntityList(objectType.getFields(), lookupConfigs);
                }
            }
        }
    }


    private HashMap<String, LookupConfig> gallaryLookupConfigs(JsonNode formObject) {
        HashMap<String, LookupConfig> lookupConfigs = new HashMap<>();

        JsonNode jsonNodeModule = formObject.get("module");

        JsonNode components = jsonNodeModule.get("components");
        if (components != null) {

            List<JSONObject> lookupJsonObjects = new ArrayList<>();
            rescureGetLookup((JSONArray) JSONPath.extract(components.toString(), "$..contents"), lookupJsonObjects);


            //JsonNode lookupsInField = components.get("$..fields[?(@.editor.type=='LookupEdit')].editor");
            List<JSONObject> lookupsInField = new ArrayList<>();
            rescureGetLookupEditor((JSONArray) JSONPath.extract(components.toString(), "$..fields"), lookupsInField);

            List<JSONObject> lookupsInColumns = new ArrayList<>();
            rescureGetLookupEditor((JSONArray) JSONPath.extract(components.toString(), "$..columns"), lookupsInColumns);

            //JsonNode lookupsInFilterBar = components.get("$..fieldConfigs[?(@.control.controltype=='help')].control");
            List<JSONObject> lookupsInFilterBar = new ArrayList<>();
            rescureGetLookupControl((JSONArray) JSONPath.extract(components.toString(), "$..fieldConfigs"), lookupsInFilterBar);


            //JsonNode popupLookupsInFilterBar = components.get("$..fieldConfigs[?(@.control.controltype=='combolist-help')].control");
            List<JSONObject> popupLookupsInFilterBar = new ArrayList<>();
            rescureGetComboListHelpControl((JSONArray) JSONPath.extract(components.toString(), "$..fieldConfigs"), popupLookupsInFilterBar);

            //JsonNode lookupsInCompactFilterBar = components.get("$..filterList[?(@.control.controltype=='help')].control");
            List<JSONObject> lookupsInCompactFilterBar = new ArrayList<>();
            rescureGetHelpControl((JSONArray) JSONPath.extract(components.toString(), "$..filterList"), lookupsInCompactFilterBar);

            if (lookupJsonObjects != null) {
                lookupJsonObjects.forEach((lookupConfig) -> this.appendLookupConfigs(lookupConfigs, lookupConfig));
            }

            if (lookupsInColumns != null) {
                lookupsInColumns.forEach((lookupConfig) -> this.appendLookupConfigs(lookupConfigs, lookupConfig));
            }

            if (lookupsInField != null) {
                lookupsInField.forEach((lookupConfig) -> this.appendLookupConfigs(lookupConfigs, lookupConfig));
            }

            if (lookupsInFilterBar != null) {
                lookupsInFilterBar.forEach((lookupConfig) -> this.appendLookupConfigsByFilterBar(lookupConfigs, lookupConfig));
            }

            if (popupLookupsInFilterBar != null) {
                popupLookupsInFilterBar.forEach((lookupConfig) -> this.appendLookupConfigsByFilterBar(lookupConfigs, lookupConfig));
            }

            if (lookupsInCompactFilterBar != null) {
                lookupsInCompactFilterBar.forEach((lookupConfig) -> this.appendLookupConfigsByFilterBar(lookupConfigs, lookupConfig));
            }

        }
        return lookupConfigs;
    }

    private void rescureGetHelpControl(JSONArray jsonArray, List<JSONObject> jsonObjectList) {
        if (jsonArray == null || jsonArray.size() == 0) {
            return;
        }

        jsonArray.forEach((item) -> {
            if (item instanceof JSONArray) {
                JSONArray itemJsonArray = (JSONArray) item;
                rescureGetHelpControl(itemJsonArray, jsonObjectList);
            } else if (item instanceof JSONObject) {
                JSONObject jsonObjectItem = (JSONObject) item;
                if (jsonObjectItem.get("control") != null) {
                    JSONObject editorJsonObject = (JSONObject) jsonObjectItem.get("control");
                    if (editorJsonObject != null && "help".equals(editorJsonObject.get("controltype").toString())) {
                        jsonObjectList.add(editorJsonObject);
                    }
                }
                if (jsonObjectItem.get("filterList") instanceof JSONArray) {
                    JSONArray contentsJsonArray = (JSONArray) jsonObjectItem.get("filterList");
                    rescureGetHelpControl(contentsJsonArray, jsonObjectList);
                }
            }
        });
    }

    private void rescureGetComboListHelpControl(JSONArray jsonArray, List<JSONObject> jsonObjectList) {
        if (jsonArray == null || jsonArray.size() == 0) {
            return;
        }

        jsonArray.forEach((item) -> {
            if (item instanceof JSONArray) {
                JSONArray itemJsonArray = (JSONArray) item;
                rescureGetComboListHelpControl(itemJsonArray, jsonObjectList);
            } else if (item instanceof JSONObject) {
                JSONObject jsonObjectItem = (JSONObject) item;
                if (jsonObjectItem.get("control") != null) {
                    JSONObject editorJsonObject = (JSONObject) jsonObjectItem.get("control");
                    if (editorJsonObject != null && "combolist-help".equals(editorJsonObject.get("controltype").toString())) {
                        jsonObjectList.add(editorJsonObject);
                    }
                }
                if (jsonObjectItem.get("fieldConfigs") instanceof JSONArray) {
                    JSONArray contentsJsonArray = (JSONArray) jsonObjectItem.get("fieldConfigs");
                    rescureGetComboListHelpControl(contentsJsonArray, jsonObjectList);
                }
            }
        });
    }

    private void rescureGetLookupControl(JSONArray jsonArray, List<JSONObject> jsonObjectList) {
        if (jsonArray == null || jsonArray.size() == 0) {
            return;
        }

        jsonArray.forEach((item) -> {
            if (item instanceof JSONArray) {
                JSONArray itemJsonArray = (JSONArray) item;
                rescureGetLookupControl(itemJsonArray, jsonObjectList);
            } else if (item instanceof JSONObject) {
                JSONObject jsonObjectItem = (JSONObject) item;
                if (jsonObjectItem.get("control") != null) {
                    JSONObject editorJsonObject = (JSONObject) jsonObjectItem.get("control");
                    if (editorJsonObject != null && "help".equals(editorJsonObject.get("controltype").toString())) {
                        jsonObjectList.add(editorJsonObject);
                    }
                }
                if (jsonObjectItem.get("fieldConfigs") instanceof JSONArray) {
                    JSONArray contentsJsonArray = (JSONArray) jsonObjectItem.get("fieldConfigs");
                    rescureGetLookupControl(contentsJsonArray, jsonObjectList);
                }
            }
        });
    }

    private void rescureGetLookupEditor(JSONArray jsonArray, List<JSONObject> jsonObjectList) {
        if (jsonArray == null || jsonArray.size() == 0) {
            return;
        }

        jsonArray.forEach((item) -> {
            if (item instanceof JSONArray) {
                JSONArray itemJsonArray = (JSONArray) item;
                rescureGetLookupEditor(itemJsonArray, jsonObjectList);
            } else if (item instanceof JSONObject) {
                JSONObject jsonObjectItem = (JSONObject) item;
                if (jsonObjectItem.get("editor") != null) {
                    JSONObject editorJsonObject = (JSONObject) jsonObjectItem.get("editor");
                    if (editorJsonObject != null && editorJsonObject.containsKey("type") && this.isLookupEdit(StringUtils.getSpecialValue(editorJsonObject.get("type")))) {
                        jsonObjectList.add(editorJsonObject);
                    }
                }
                if (jsonObjectItem.get("fields") instanceof JSONArray) {
                    JSONArray contentsJsonArray = (JSONArray) jsonObjectItem.get("fields");
                    rescureGetLookupEditor(contentsJsonArray, jsonObjectList);
                }
            }
        });
    }

    private void rescureGetLookup(JSONArray jsonArray, List<JSONObject> jsonObjectList) {
        if (jsonArray == null || jsonArray.size() == 0) {
            return;
        }

        jsonArray.forEach((item) -> {
            if (item instanceof JSONArray) {
                JSONArray itemJsonArray = (JSONArray) item;
                rescureGetLookup(itemJsonArray, jsonObjectList);
            } else if (item instanceof JSONObject) {
                JSONObject jsonObjectItem = (JSONObject) item;
                if (this.isLookupEdit(StringUtils.getSpecialValue(jsonObjectItem.get("type")))) {
                    jsonObjectList.add(jsonObjectItem);
                }
                if (jsonObjectItem.get("contents") instanceof JSONArray) {
                    JSONArray contentsJsonArray = (JSONArray) jsonObjectItem.get("contents");
                    rescureGetLookup(contentsJsonArray, jsonObjectList);
                }
            }
        });
    }

    private void appendLookupConfigs(HashMap<String, LookupConfig> lookupConfigs, JSONObject lookupJsonObject) {
        if (lookupJsonObject == null) {
            return;
        }
        JsonNode lookup = SerializeUtility.getInstance().readTree(lookupJsonObject.toJSONString());

        // 如果获取到帮助id为空那么返回
        if (lookup.get("helpId") == null) {
            return;
        }

        String helpId = StringUtils.getSpecialValue(lookup.get("helpId"));

        JsonNode lookupDataSource = lookup.get("dataSource");
        String uri = null;
        if (lookupDataSource != null) {
            uri = StringUtils.getSpecialValue(lookup.get("dataSource").get("uri"));
        }
        if (!StringUtility.isNullOrEmpty(helpId) && !StringUtility.isNullOrEmpty(uri)) {
            if (!lookupConfigs.containsKey(uri)) {
                LookupConfig tempVar = new LookupConfig();
                tempVar.setUri(String.join("/", uri.split("[.]", -1)));
                tempVar.setLookupId(helpId);
                tempVar.setLinkConfig(ExtractHelpLinkConfigFromJsonObject.extract(lookupJsonObject));
                lookupConfigs.put(uri, tempVar);
            }
        }
    }

    private void appendLookupConfigsByFilterBar(HashMap<String, LookupConfig> lookupConfigs, JSONObject lookup) {
        if (lookup == null) {
            return;
        }

        String helpId = StringUtils.getSpecialValue(lookup.get("helpId"));
        String uri = StringUtils.getSpecialValue(lookup.get("uri"));
        if (!StringUtility.isNullOrEmpty(helpId) && !StringUtility.isNullOrEmpty(uri)) {
            if (!lookupConfigs.containsKey(uri)) {
                LookupConfig tempVar = new LookupConfig();
                tempVar.setUri(String.join("/", uri.split("[.]", -1)));
                tempVar.setLookupId(helpId);
                tempVar.setLinkConfig(ExtractHelpLinkConfigFromJsonObject.extract(lookup));
                lookupConfigs.put(uri, tempVar);
            }
        }
    }

    /**
     * 判断是否为帮助控件
     */
    private boolean isLookupEdit(String editorType) {
        return editorType.equals("LookupEdit") || editorType.equals("PersonnelSelector") || editorType.equals("OrganizationSelector");
    }

}
